﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Collections;

namespace Mota.XMLControls
{
    public class DBTreeView:TreeView
    {
		public DBTreeView()
		{
			// This call is required by the Windows.Forms Form Designer.
			InitializeComponent();
			//TODO Add any initialization after the InitComponent call

		}

		protected override void Dispose( bool disposing )
		{
			base.Dispose( disposing );
		}

		private void InitializeComponent()
		{

		}

		protected override void OnPaint(PaintEventArgs pe)
		{
			base.OnPaint(pe);
		}

		private bool m_autoBuild = true;

		public bool AutoBuildTree
		{
			get
			{
				return this.m_autoBuild;
			}
			set
			{
				this.m_autoBuild = value;
			}
		}

		#region Data Binding
		private CurrencyManager m_currencyManager = null;
		private String m_ValueMember;
		private String m_DisplayMember;
        private String m_ParentMember;
		private object m_oDataSource;

		public object DataSource
		{
			get
			{
				return m_oDataSource;

			}
			set
			{
				if ( value == null )
				{
					this.m_currencyManager = null;
					this.Nodes.Clear();
				}
				else
				{
					if ( !(value is IList || m_oDataSource is IListSource ) )
						throw (new System.Exception ("Invalid DataSource"));
					else
					{
						if ( value is IListSource )
						{
							IListSource myListSource = (IListSource) value;
                            //if collection of collections throw exception
							if ( myListSource.ContainsListCollection == true )
								throw (new System.Exception ("Invalid DataSource"));
						}
						this.m_oDataSource = value;
						this.m_currencyManager = (CurrencyManager) this.BindingContext[value];
						if ( this.AutoBuildTree )
							BuildTree();
					}
				}
			}
		}

        public string ValueMember
        {
            get
            {
                return this.m_ValueMember;
            }
            set
            {
                this.m_ValueMember = value;
            }
        }

        public string ParentMember
        {
            get
            {
                return this.m_ParentMember;
            }
            set
            {
                this.m_ParentMember = value;
            }
        }

		public string DisplayMember
		{
			get
			{
				return this.m_DisplayMember;
			}
			set
			{
				this.m_DisplayMember = value;
			}
		}

		public object GetValue(int index)
		{
			IList innerList = this.m_currencyManager.List;
			if ( innerList != null )
			{
				if ( (this.ValueMember != "") && (index >= 0 && index < innerList.Count))
				{
					PropertyDescriptor pdValueMember;
					pdValueMember = this.m_currencyManager.GetItemProperties()[this.ValueMember];
					return pdValueMember.GetValue(innerList[index]);
				}
			}
			return null;
		}

		public object GetDisplay(int index)
		{
			IList innerList = this.m_currencyManager.List;
			if ( innerList != null )
			{
				if ( (this.DisplayMember != "") && (index >= 0 && index < innerList.Count))
				{
					PropertyDescriptor pdDisplayMember;
                    pdDisplayMember = this.m_currencyManager.GetItemProperties()[this.DisplayMember];
					return pdDisplayMember.GetValue (innerList[index]);
				}
			}
			return null;
		}

		#endregion

		#region Building the Tree

		private ArrayList treeGroups = new ArrayList();

		public void BuildTree()
		{
			this.Nodes.Clear();
			if ( (this.m_currencyManager != null) && (this.m_currencyManager.List != null) )
			{
				IList innerList = this.m_currencyManager.List;

                List<DBTreeNode> listNode = new List<DBTreeNode>();
                this.BeginUpdate();
                for (int i = 0; i < this.m_currencyManager.Count;i++ )
                {
                    this.m_currencyManager.Position = i;
 
                    PropertyDescriptor pdParent;
                    PropertyDescriptor pdValue;
                     PropertyDescriptor pdDisplay;
 
                    pdValue = this.m_currencyManager.GetItemProperties()[this.ValueMember];
                    pdParent = this.m_currencyManager.GetItemProperties()[this.ParentMember];
                    pdDisplay = this.m_currencyManager.GetItemProperties()[this.DisplayMember];

                    object currObject = innerList[this.m_currencyManager.Position];
                    object nodeValue = pdValue.GetValue(this.m_currencyManager.Current);
                    object parentValue = pdParent.GetValue(this.m_currencyManager.Current);
                    String displayValue = pdDisplay.GetValue(this.m_currencyManager.Current).ToString();
                    listNode.Add(new DBTreeNode(nodeValue, displayValue, parentValue, this.m_currencyManager.Current));
                }
                IEnumerable<SupportNode> result = SupportNode.BuildTreeAndGetRoots(listNode);

                foreach (SupportNode sn in result)
                {
                    this.Nodes.Add(sn.AssociatedObject);
                    AddNodes(sn.Children, sn.AssociatedObject);
                }

                this.EndUpdate();

				if ( this.Nodes.Count > 0 )
                    //select first node
					this.SelectedNode = this.Nodes[0];
			}
		}
        private void AddNodes(IEnumerable<SupportNode> nodes,TreeNode parent)
        {
                foreach (SupportNode sn in nodes)
                {
                    parent.Nodes.Add(sn.AssociatedObject);
                    AddNodes(sn.Children,sn.AssociatedObject);
                }
        }
		#endregion


		public void SetLeafData(String name, String displayMember, String valueMember, 
			int imageIndex, int selectedImageIndex)
		{
			this.Tag = name;
			this.DisplayMember = displayMember;
			this.ValueMember = valueMember;
			this.ImageIndex = imageIndex;
			this.SelectedImageIndex = selectedImageIndex;
		}

		public bool IsLeafNode (TreeNode node)
		{
			return (node.Nodes.Count == 0);
		}

		#region Keeping Everything In Sync

		public TreeNode FindNodeByValue (object value)
		{
			return FindNodeByValue (value, this.Nodes);
		}

		public TreeNode FindNodeByValue (object Value, TreeNodeCollection nodesToSearch)
		{
			int i = 0;
			TreeNode currNode;

			while ( i < nodesToSearch.Count )
			{
				currNode = nodesToSearch[i];
				i ++;

                if (String.Compare((currNode as DBTreeNode).NodeValue.ToString(), Value.ToString(), true) == 0)
                {
                    return currNode;
                }
				if(currNode.Nodes.Count>0)
                {
                    TreeNode successNode = FindNodeByValue (Value, currNode.Nodes);
                    if (successNode != null)
                        return successNode;
				}
			}

			return null;
		}
			
		
		protected override void OnAfterSelect(TreeViewEventArgs e)
		{
//             DBTreeNode leafNode = (DBTreeNode)e.Node;
// 
// 			if (leafNode != null)
// 			{
// 				if ( this.m_currencyManager.Position != leafNode.Position )
// 					this.m_currencyManager.Position = leafNode.Position;
// 			}
// 			// TODO:  Add MyTreeViewCtrl.OnAfterSelect implementation
 			base.OnAfterSelect (e);
		}

		#endregion
    }

    class SupportNode
    {
        public List<SupportNode> Children = new List<SupportNode>();
        public SupportNode Parent { get;set; }
        public DBTreeNode AssociatedObject {get;set;}

        public static IEnumerable<SupportNode> BuildTreeAndGetRoots(List<DBTreeNode> actualObjects)
        {
            Dictionary<String, SupportNode> lookup = new Dictionary<String, SupportNode>();
            actualObjects.ForEach(x => lookup.Add(x.NodeValue.ToString(), new SupportNode { AssociatedObject = x }));
            foreach(var item in lookup.Values)
            {
                SupportNode proposedParent;
                if(lookup.TryGetValue(item.AssociatedObject.ParentValue.ToString(),out proposedParent)){
                    item.Parent = proposedParent;
                    proposedParent.Children.Add(item);
                }
            }
            return lookup.Values.Where(x => x.Parent == null);
        }
    }
}
